from datetime import date, timedelta, datetime

import xlsxwriter
from flask import jsonify, request, send_file, g

from aab import app
from aab.exceptions import AabException
from aab.model import Group, User, DailyState, CoPerson, Route, db, Roles
from aab.utils import success, format_health, format_persons, fill_worksheet, login_required, update_persons, \
    jsonify_error, DATE_FORMAT, SheetData


@app.route('/group/<group_name>/status', methods=['GET'])
@app.route('/group/<group_name>/status/<day>', methods=['GET'])
@login_required(roles=[Roles.ADMIN, Roles.GROUP_MANAGER])
def query_group_status(group_name, day=None):
    group = Group.query.filter_by(name=group_name).scalar()
    if group is None:
        return jsonify_error(404, '组不存在')

    users = User.query.filter_by(group=group).order_by(User.number).all()
    day = day or date.today()
    return jsonify(_users_status_list(users, day))


def _users_status_list(users, day):
    result = []
    for user in users:
        daily_status = DailyState.query.filter_by(user=user, day=day).scalar()
        if daily_status:
            d = _daily_status_to_dict(daily_status)
        else:
            d = {'status': '未填写'}
        d['user_number'] = user.number
        result.append(d)

    return result


@app.route('/status', methods=['GET'])
@app.route('/status/<day>', methods=['GET'])
@login_required(roles=[Roles.ADMIN])
def query_all_status(day=None):
    users = User.query.order_by(User.number).all()
    day = day or date.today()
    return jsonify(_users_status_list(users, day))


@app.route('/user/<user_number>/status-for-update', methods=['GET'])
@app.route('/user/<user_number>/status-for-update/<day>', methods=['GET'])
@login_required
def query_today_state_for_update(user_number, day=None):
    if day is None:
        day = date.today()
    else:
        day = datetime.strptime(day, DATE_FORMAT)
    return jsonify(query_state(user_number, for_update=True, day=day))


@app.route('/user/<user_number>/status', methods=['GET'])
@app.route('/user/<user_number>/status/<day>', methods=['GET'])
@login_required
def query_today_state(user_number, day=None):
    if day is None:
        day = date.today()
    else:
        day = datetime.strptime(day, DATE_FORMAT)
    return jsonify(query_state(user_number, for_update=False, day=day))


def query_state(user_number, for_update=False, day=None):
    user = User.query.filter_by(number=user_number).scalar()
    if user is None:
        raise AabException(404, message="User {} NOT FOUND".format(user_number))
    state = do_query_state(user, for_update, day)
    if state is None:
        raise AabException(404, message='Daily state not found')

    return state


def do_query_state(user, for_update, day=None):
    day = day or date.today()
    daily_state = DailyState.query.filter_by(user_id=user.id, day=day).scalar()
    if daily_state is None:
        if for_update:
            day = day - timedelta(days=1)
            daily_state = DailyState.query.filter_by(user_id=user.id, day=day).scalar()
        if daily_state is None:
            return None

    return _daily_status_to_dict(daily_state)


def _daily_status_to_dict(daily_state):
    result = daily_state.to_dict()

    result['is_temperature_normal'] = daily_state.is_temperature_normal

    persons = CoPerson.query.filter_by(daily_state_id=daily_state.id).all()
    persons = [person.to_dict() for person in persons]
    result['persons'] = persons

    routes = Route.query.filter_by(daily_state_id=daily_state.id).all()
    routes = [route.to_dict() for route in routes]
    result['routes'] = routes

    return result


@app.route('/user/<user_number>/status', methods=['PUT'])
@login_required
def fill_today_state(user_number):
    do_fill_state(user_number, date.today())
    return success()


@app.route('/user/<user_number>/status/<day>', methods=['PUT'])
@login_required
def fill_state(user_number, day):
    day = datetime.strptime(day, '%Y-%m-%d').date()
    do_fill_state(user_number, day)
    return success()


def do_fill_state(user_number, day):
    user = User.query.filter_by(number=user_number).scalar()
    if user is None:
        raise AabException(400, "用户不存在")

    data = request.get_json()
    if data is None:
        raise AabException(400, message='Require JSON data')

    update_state(user, data, day)
    db.session.commit()


def update_state(user, data, day):
    daily_state = DailyState.query.filter_by(user=user, day=day).scalar()
    if daily_state is None:
        daily_state = DailyState(user_id=user.id, day=day, fill_time=datetime.now())
        daily_state.update_from_dict(data)
        db.session.add(daily_state)

        if 'persons' in data:
            for person in data['persons']:
                co_person = CoPerson(daily_state=daily_state).update_from_dict(person)
                db.session.add(co_person)

        if 'routes' in data:
            for route in data['routes']:
                db.session.add(Route(daily_state=daily_state).update_from_dict(route))
    else:
        daily_state.fill_time = datetime.now()
        daily_state.update_from_dict(data)

        update_persons(data, daily_state=daily_state)

        route_entities = Route.query.filter_by(daily_state=daily_state).all()
        route_map = {r.id: r for r in route_entities}

        if 'routes' in data:
            for route in data['routes']:
                route_id = route.get('id', None)
                if route_id is not None and route_id in route_map:
                    route_map[route_id].update_from_dict(route)
                    del route_map[route_id]
                else:
                    db.session.add(Route(daily_state=daily_state).update_from_dict(route))

        for _, route in route_map.items():
            db.session.delete(route)


@app.route('/xlsx/status', methods=['GET'])
@login_required(roles=[Roles.ADMIN])
def generate_today_xlsx():
    return _generate_status_xlsx(date.today())


@app.route('/xlsx/status/<day>', methods=['GET'])
@login_required(roles=[Roles.ADMIN])
def generate_status_xlsx(day):
    day = datetime.strptime(day, '%Y-%m-%d').date()
    return _generate_status_xlsx(day)


def _generate_status_xlsx(day):
    path = '/tmp/status-{}__{}.xlsx'.format(day.strftime('%Y-%m-%d'),
                                            datetime.now().strftime('%Y-%m-%d-%H-%M-%S'))
    worksheet = SheetData(path)

    fields = [
        ('人员类别', 'status'),
        ('人员状态', 'category'),
        ('办公方式', 'office_type'),
        ('位置状态', 'addr_type'),
        ('本人健康状况', format_health),
        ('前一日体温', lambda d: '早{}；午{}；晚{}'.format(d.temperature_morning,
                                                 d.temperature_middle,
                                                 d.temperature_night)),
        ('体温是否正常', lambda d: '是' if d.is_temperature_normal else '否'),
        ('共同隔离人员情况', lambda d: format_persons(d) if d.status == '隔离' else ''),
        ('共同居住人员情况', lambda d: format_persons(d) if d.status == '非隔离' else ''),
        ('共同隔离人员是否有15日内返京的', lambda d: '是' if d.has_co_isolation_14 else '否'),
        ('居住位置是否变更', lambda d: '是' if d.address_changed else '否'),
        ('小区有无新冠肺炎病例', lambda d: '是' if d.has_coron_in_region else '否'),
        # ('保障需求-生活-5L矿泉水（桶）', 'need_water_count'),
        # ('保障需求-急需非疫情药品', 'need_medical'),
        # ('保障需求-清洁卫生用品', 'need_tool'),
        # ('保障需求-蔬菜水果（定期分类供应）', lambda d: '是' if d.need_vegetable else '否'),
        # ('保障需求-快递信息', 'need_express')
    ]

    worksheet.write(0, 0, '人员编号')
    for j, field in enumerate(fields):
        name = field[0]
        worksheet.write(0, j + 1, name)

    for i, user in enumerate(User.query.order_by(User.number)):
        worksheet.write(i + 1, 0, user.number)

        daily_price = DailyState.query.filter_by(user=user, day=day).scalar()
        if not daily_price:
            continue

        fill_worksheet(worksheet, daily_price, i, fields)

    return worksheet.send_file()


@app.route('/status/not-filled/<day>', methods=['GET'])
@app.route('/status/not-filled', methods=['GET'])
@login_required
def query_not_filled(day=None):
    if day is None:
        day = date.today()
    else:
        day = datetime.strptime(day, DATE_FORMAT)

    group = Group.query.filter_by(name=g.auth.group).scalar()
    if group is None:
        return jsonify_error(404, message='GROUP {} NOT FOUND'.format(g.auth.group))

    users = User.query.filter_by(group=group)
    not_filled = []
    for user in users:
        if user.number == g.auth.user_number:
            continue

        daily_state = DailyState.query.filter_by(day=day, user=user).scalar()
        if daily_state is None:
            not_filled.append(user.number)

    return jsonify(not_filled)

